﻿
Imports Microsoft.VisualBasic
Imports System
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Media
Imports System.Windows.Shapes
Imports System.Windows.Input
Imports System.Windows.Media.Animation
Imports System.Windows.Threading
Imports System.Timers
Imports System.Windows.Forms
Imports System.Drawing
Imports System.IO

Imports VPager.Settings

''' <summary>
''' The PagerDisplay is the main program execution class housing most
''' of the command-and-control logic.  Program code control takes over
''' by responding to the Window_Loaded event and shuts down by responding
''' to the PagerDisplay_Closed event.
''' 
''' The main responsibilites of this class are:
''' 
'''   * Intialize the pager system including creating and launching
'''     background processes and intializing logging
'''   * Rotate display (with visual fade effects) of all active page codes
'''   * Respond to changes in the active page set and update display
'''   * Respond to background tasks error events
'''   * respond to Display Window close event and shutdown application
''' 
''' After initialization, the main repsonsibility of the class is to display
''' the active page codes as provided by the PageList object and to update
''' the System Status LED on main display so that it periodically blinks
''' to indicate that the software is still executing through its main control 
''' loop and not hung up.  This is all handled through the processPageRequest()
''' method and is implemented as a series of coordinated asynchronous state 
''' transtions with wait times in between.  The asynchronous implementation is
''' used so as not to block the main UI Thread between transtions so that the
''' application overall runs more smoothly with less CPU overhead. 
''' 
''' State transitions are managed through the pageStateHandler() method.
''' 
''' </summary>
''' <remarks>
''' At some point most the command-and-control code should be moved into 
''' another non-Window class. </remarks>
Class PagerDisplay

    ' The log file Mark Stamp
    Private Const MARK_STAMP As String = "----- MARK -----"

    Private Const BLANK_PAGE As String = ""

    ' State Transition Values
    Private Const STATE_FADE_IN_DONE As Integer = 0
    Private Const STATE_FADE_OUT_DONE As Integer = 1
    Private Const STATE_PAGE_VIEW_DONE As Integer = 2
    Private Const STATE_LED_BLINK_DONE As Integer = 3

    ' -------------------------------------------------------------------------
    ' Member Variables
    ' -------------------------------------------------------------------------
    '
    ' Utility Variables
    Private mPageList As PageList
    Private mNetMonitor As NetworkMonitor
    Private mLogger As Logger
    Private mCanContinue As Boolean
    Private mNextMark As Date
    Private mLastCode As String = ""
    Private mPageBlock As TextBlock
    Private mFadeInAnimation As DoubleAnimation
    Private mFadeOutAnimation As DoubleAnimation
    Private mNextPageState As Integer
    Private mLayoutMgr As LayoutManager
    Private mStatusMgr As StatusManager
    Private mViewTimer As DispatcherTimer
    Private mBlinkTimer As DispatcherTimer

    ' -------------------------------------------------------------------------
    ' Property Definitions
    ' -------------------------------------------------------------------------
    '
    Public ReadOnly Property PageBlock
        Get
            Return mPageBlock
        End Get
    End Property

    Public ReadOnly Property logger As Logger
        Get
            Return mLogger
        End Get
    End Property

    Public ReadOnly Property PageList As PageList
        Get
            Return mPageList
        End Get
    End Property

    ' -------------------------------------------------------------------------
    ' Main Code
    ' -------------------------------------------------------------------------

    Private Sub PagerDisplay_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Closed
        ' Close Window
        mLogger.info("Main Display Closed.")
        ' Shutdown background threads
        mCanContinue = False
        mNetMonitor.stopMonitoring()
        mPageList.EnableAutoRefresh = False
    End Sub

    Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
        ' Start the system up
        startPagerSystem()
    End Sub

    Private Sub initialize()

        mPageBlock = FindName("tbkPage")

        ' Get Logging up as soon as possible in case we need to log fatal errors.
        mLogger = LoggerFactory.getLogger("Pager")
        mLogger.LogFilePath = SettingsManager.getValue(SettingKey.LOG_FILE)
        mLogger.LogLevel = SettingsManager.getValue(SettingKey.LOG_LEVEL)

        mLogger.info("Pager System Started.  Version " & Application.AppVersion)

        ' Setup Text Fade animations used to transition visually between
        ' multiple page codes.
        Dim transTime As Double = SettingsManager.getValue(SettingKey.TRANSITION_TIME)
        Dim fadeTime As Double = transTime / 2

        mFadeInAnimation = New DoubleAnimation(0, 1, New Duration(TimeSpan.FromSeconds(fadeTime)))
        AddHandler mFadeInAnimation.Completed, AddressOf pageStateHandler

        mFadeOutAnimation = New DoubleAnimation(1, 0, New Duration(TimeSpan.FromSeconds(fadeTime)))
        AddHandler mFadeOutAnimation.Completed, AddressOf pageStateHandler

        ' Setup the page view timer that signals when the next page transition
        ' should take place.
        Dim pagePause As Integer = SettingsManager.getValue(SettingKey.PAGE_PAUSE)
        mViewTimer = New DispatcherTimer()
        mViewTimer.Interval = New TimeSpan(0, 0, pagePause) 'Hours, minutes, Seconds
        AddHandler mViewTimer.Tick, AddressOf pageStateHandler

        ' Setup the System Status LED Blink timer.
        mBlinkTimer = New DispatcherTimer()
        mBlinkTimer.Interval = New TimeSpan(2500000) '100-Nanosecond Units ~0.25 Secs
        AddHandler mBlinkTimer.Tick, AddressOf pageStateHandler

        ' Create our LayoutManager that will layout the main display (Me)
        mLayoutMgr = New LayoutManager(Me)

        ' Create the StatusManger which owns the indidivual StatusLED objects.
        mStatusMgr = New StatusManager()
        ' Associated the LED UI Elements owned by the LayoutManager with its
        ' control logic owned by the StatusManager.
        mStatusMgr.SystemStatus.setUIElement(mLayoutMgr.SystemLED)
        mStatusMgr.NetworkStatus.setUIElement(mLayoutMgr.NetworkLED)
        mStatusMgr.PageStatus.setUIElement(mLayoutMgr.PageLED)

        ' Perform initial Display Layout.
        mLayoutMgr.configureMainDisplay()

        mCanContinue = True

        ' Create our PageList object that parses active page codes from the
        ' page code source file.
        Dim pagerFile As String = SettingsManager.getValue(SettingKey.PAGER_FILE)
        mPageList = New PageList(pagerFile, False)
        mPageList.EnableAutoRefresh = True
        ' Add handlers so that change and error events can be responded to.
        AddHandler mPageList.ErrorEvent, AddressOf pageListErrorHandler
        AddHandler mPageList.Changed, AddressOf pageListChangeHandler
        mPageList.refresh()

        ' Create and configure our NetworkMonitor
        mNetMonitor = New NetworkMonitor(
            SettingsManager.getValue(SettingKey.PING_FREQUENCY),
            SettingsManager.getValue(SettingKey.NET_ALARM_THRESHOLD))
        mNetMonitor.PingTargets = SettingsManager.getValue(SettingKey.PING_TARGETS)
        Dim t As Integer = SettingsManager.getValue(SettingKey.PING_TIMEOUT)
        mNetMonitor.PingTimeout = t * 1000 ' convert seconds to milliseconds

        AddHandler mNetMonitor.StatusChanged, AddressOf networkChangeHandler
        mNetMonitor.startMontoring()

        ' Seed our next mark time
        mNextMark = Date.Now.AddSeconds(
            SettingsManager.getValue(SettingKey.MARK_TIME))

    End Sub

    Private Sub setPageCode(ByVal code As String)
        mPageBlock.Text = code
    End Sub

    ''' <summary>
    ''' This is main state transition control code implemented as an event handler.
    ''' In order to keep the UI responsive state transtions during the paging cycle
    ''' are implemented as asynchronous actions.  Each asychronous action indicates
    ''' its completion by firing an event which is handled by this method.  Prior to 
    ''' execuitng the asynch action the next state transition is identifed by setting
    ''' the mNextPageState member variable to one of the STATE_* value constants.
    ''' </summary>
    ''' <param name="sender">Not used. Required part of event handler signature.</param>
    ''' <param name="e">Not used. Required part of event handler signature.</param>
    ''' <remarks></remarks>
    Private Sub pageStateHandler(ByVal sender As Object, ByVal e As EventArgs)

        If mNextPageState = STATE_FADE_IN_DONE Then
            ' Need to wait 3 seconds (display time) then Fade Out
            waitForPageToDisplay()

        ElseIf mNextPageState = STATE_FADE_OUT_DONE Then
            Dim code As String = mPageList.nextItem
            setPageCode(code)
            mLastCode = code
            fadePageIn()

        ElseIf mNextPageState = STATE_PAGE_VIEW_DONE Then
            ' Forcing the CommandManager to raise the RequerySuggested event
            ' This is required to place the timer back into the Dispatch Queue
            CommandManager.InvalidateRequerySuggested()
            mViewTimer.Stop()

            ' Take opportunity to blink the System Status LED on main display
            ' by turning it off briefly and using the Blink Timer to add delay
            mStatusMgr.SystemStatus.turnLedOff()
            mNextPageState = STATE_LED_BLINK_DONE
            mBlinkTimer.Start()

        ElseIf mNextPageState = STATE_LED_BLINK_DONE Then
            mBlinkTimer.Stop()
            processSystemTasks()
            processPageRequests()

        End If

    End Sub

    Private Sub fadePageIn()
        ' Fade Code into view
        mNextPageState = STATE_FADE_IN_DONE
        mPageBlock.BeginAnimation(UIElement.OpacityProperty, mFadeInAnimation)
    End Sub

    Private Sub fadePageOut()
        ' Fade Code out of view
        mNextPageState = STATE_FADE_OUT_DONE
        mPageBlock.BeginAnimation(UIElement.OpacityProperty, mFadeOutAnimation)
    End Sub

    Private Sub waitForPageToDisplay()
        If mCanContinue Then
            ' Activate the view timer to trigger next transition. Timer duration = view duration
            mNextPageState = STATE_PAGE_VIEW_DONE

            mStatusMgr.SystemStatus.turnLedOn()

            mViewTimer.Start()
        Else
            mLogger.info("Exiting main paging control loop.")
        End If

    End Sub


    Sub startPagerSystem()

        Try
            initialize()
            clearDisplay()
            processPageRequests()

        Catch ex As Exception
            If Not IsNothing(mLogger) Then
                mLogger.fatal("Unhandled Exception caught. " & ex.Message & vbNewLine & ex.StackTrace.ToString)
                mLogger.fatal("Exiting application.")
            End If
            Exit Sub

        End Try

        Exit Sub

    End Sub

    Sub processPageRequests()

        Dim strCode As String

        If mCanContinue Then
            If Not mPageList.isEmpty Then
                mStatusMgr.PageStatus.turnLedOn()
                strCode = mPageList.peek
                If Not strCode = mLastCode Then
                    fadePageOut()
                Else
                    mPageList.nextItem()
                    waitForPageToDisplay()
                End If
            Else
                If Not mPageList.hasErrors Then
                    mStatusMgr.PageStatus.turnLedOff()
                Else
                    mStatusMgr.PageStatus.turnLedOn()
                End If
                mLastCode = ""
                clearDisplay()
                waitForPageToDisplay()
            End If
        Else
            mLogger.info("Exiting main paging control loop.")
        End If

    End Sub

    Private Sub clearDisplay()
        setPageCode(BLANK_PAGE)
    End Sub

    Private Sub processSystemTasks()

        checkMark()

    End Sub

    Sub checkMark()

        If Date.Compare(Date.Now, mNextMark) > 0 Then
            mLogger.info(MARK_STAMP)
            mNextMark = Date.Now.AddSeconds(SettingsManager.getValue(SettingKey.MARK_TIME))
        End If

    End Sub

    Private Sub pageListErrorHandler(ByVal sender As PageList, ByVal e As PageList.PageListEventArgs)
        If sender.hasErrors Then
            If sender.hasFileErrors Then
                mStatusMgr.PageStatus.Status = StatusLED.StatusCode.Err
                mLogger.err(e.Message)
            ElseIf sender.hasCodeErrors Then
                mStatusMgr.PageStatus.Status = StatusLED.StatusCode.Warning
                mLogger.warn(e.Message)
            End If
        Else
            ' Errors have cleared
            mStatusMgr.PageStatus.Status = StatusLED.StatusCode.Okay
            Dim m As String = "Page List Issues Cleared."
            If e.Message.Length > 0 Then
                m = e.Message
            End If
            mLogger.info(m)
        End If
    End Sub

    Private Sub pageListChangeHandler(ByVal sender As Object, ByVal e As EventArgs)
        Dim list = CType(sender, PageList)
        Dim codes As String = "<EMPTY>"
        If Not list.isEmpty Then
            codes = list.toString()
        End If
        mLogger.info("Active Page Code List: " & codes)
    End Sub

    Private Sub networkChangeHandler(ByVal sender As NetworkMonitor, ByVal e As NetworkMonitor.NetworkEventArgs)
        ' Network Status Change
        ' Update Status LED and log change
        If e.Status = NetworkMonitor.NetworkStatus.Okay Then
            mStatusMgr.NetworkStatus.Status = StatusLED.StatusCode.Okay
            mLogger.info(e.Message)
        ElseIf e.Status = NetworkMonitor.NetworkStatus.Warning Then
            mStatusMgr.NetworkStatus.Status = StatusLED.StatusCode.Warning
            mLogger.warn(e.Message)
        ElseIf e.Status = NetworkMonitor.NetworkStatus.Err Then
            mStatusMgr.NetworkStatus.Status = StatusLED.StatusCode.Err
            mLogger.err(e.Message)
        End If
        mStatusMgr.NetworkStatus.turnLedOn()
    End Sub


    Public Sub New()

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.

    End Sub
End Class
